/**
 * 判断元素在数组/字符串中的位置，增强功能，兼容空值的情况
 *
 * @param list - 数组
 * @param ele - 元素
 * @returns {number} 返回元素在数组的位置
 */
const indexOf = (list, ele) => {
    return list == null ? -1 : list.indexOf(ele);
}

/**
 * 判断数组中是否包含某个元素
 *
 * @param list - 数组
 * @param ele - 元素
 * @returns 指定的值是否在数组中
 */
const contain = (list, ele) => {
    return list == null ? false : list.includes(ele);
}

/**
 * 判断数组中是否包含某个元素
 *
 * @param list - 数组
 * @param ele - 元素
 * @returns 指定的值是否在数组中
 */
const notContain = (list, ele) => {
    return list == null ? true : !list.includes(ele);
}

/**
 * 移除数组中的第一个元素
 *
 * @param list - 数组
 * @param ele - 元素
 * @returns 如果成功删除，则返回 true
 */
const removeElement = (list, ele) => {
    let idx = indexOf(list, ele);
    if (idx < 0) {
        return false;
    } else {
        list.splice(idx, 1);
        return true;
    }
}

/**
 * 过滤返回数组中满足条件的一个元素，如果需要返回数组，请使用 array.filter
 *
 * @param list - 数组
 * @param filter - 过滤回调，返回true的时候保留
 * @returns element of list
 */
const findElement = (list, filter) => {
    if (list != null && list.length > 0) {
        return list.find(filter);
    }
}

/**
 * 过滤返回数组中满足条件的元素，以array的形式返回
 *
 * @param list - 数组
 * @param filter - 过滤回调，返回true的时候保留
 * @returns 满足elements of list
 */
const findElements = (list, filter) => {
    if (list == null || list.length === 0) {
        return [];
    } else {
        return list.filter(filter);
    }
}

/**
 * 过滤返回数组中满足条件的一个元素，并且取出该元素的字段
 *
 * @param list - 数组
 * @param field - 指定的字段名
 * @param filter - 过滤回调，返回true的时候保留
 * @returns 第一个满足条件的元素的字段值
 */
const findElementField = (list, field, filter) => {
    if (list != null && list.length > 0) {
        let ret = list.find(filter);
        if (ret !== undefined) {
            return ret[field];
        }
    }
}

/**
 * 模糊查找
 *
 * 根据关键字模糊查找满足条件的元素，不论如何都会构建一个新的数组返回
 *
 * @param list - 数组
 * @param keyword - 关键字
 * @param fields - 指定需要比较的字段，默认比较所有字符串
 * @returns 符合条件的数据列表
 */
const findByKeyword = (list, keyword, ...fields) => {
    if (list == null || list.length === 0) {
        return [];
    } else if (keyword == null || keyword.length === 0) {
        return [...list];
    } else if (fields.length > 0) {
        // 如果已经明确需要查找的字段，则跳过类型检测，直接进行比对
        return list.filter(item => {
            for (let field of fields) {
                if (item[field].includes(keyword)){
                    return true;
                }
            }
            return false;
        });
    } else {
        const first = list[0];
        if(typeof first === 'string'){
            return list.filter(item => item.includes(keyword));
        } else {
            // 如果没有指定字段，使用对象所有可枚举属性
            const keys = Object.keys(first);
            return list.filter(item => {
                // 如果没有指定字段，使用对象所有可枚举属性
                for (const key of keys) {
                    const value = item[key];
                    if (typeof value === 'string' && value.includes(keyword)) {
                        return true;
                    }
                }
                return false;
            });
        }
    }
}

/**
 * 查找区间内的元素
 *
 * 与 slice() 函数相比，多了对数组自身的非空判断
 *
 * @param list {[]|undefined}   数组
 * @param start {Number}        起始序号
 * @param end {Number}          结束序号（不包含）
 * @returns {[]}
 */
const findElementsByRange = (list, start, end = undefined) => {
    if (list == null || list.length === 0) {
        return [];
    } else {
        return list.slice(start, end);
    }
}

/**
 * 重新打包数组中的每一个元素
 *
 * 返回的数组可能要比原数组短，注意这与 array.map()的行为不一样
 *
 * @param list {[]|undefined}       数组
 * @param call {Function}           格式化函数
 * @returns {[]}
 */
const wrapArray = (list, call) => {
    if (list == null || list.length === 0) {
        return [];
    } else {
        let ret = [], ele;
        for (let i = 0; i < list.length; i++) {
            ele = call(list[i]);
            if (ele !== undefined) {
                ret.push(ele);
            }
        }
        return ret;
    }
}

/**
 * 按照某个字段对数组进行分组，返回一个对象
 *
 * @param list - 数组
 * @param field - 字段
 * @return 分组之后的对象
 */
const groupByField = (list, field) => {
    let ret = {};
    if (list != null && list.length > 0) {
        list.forEach(item => {
            const key = item[field];

            if (!ret[key]) {
                ret[key] = [];
            }
            ret[key].push(item);
        });
    }
    return ret;
}

/**
 * 获取最顶级的元素
 *
 * 顶级指的是最大值，还是最小值，取决于比较函数
 *
 * @example 取最大值写法
 *  ```typescript
 *  sortArray([3, 1, 2], (a, b) => a - b);
 *  ```
 *
 * @param list - 要排序的数组（不会被修改，会返回新数组）
 * @param compare - 比较函数，接受两个参数并返回一个数字：
 *   - 返回负数：使用 b 值
 *   - 返回正数：使用 a 值
 *   - 返回 0：使用 a 值
 * @returns 最顶级的元素，如果存在相同的，则取第一个
 */
const findTopElement = (list, compare) => {
    if (list == null) {
        return;
    } else if (list.length === 1) {
        return list[0];
    } else {
        let top = list[0];
        for (let i = 1, len = list.length; i < len; i++) {
            if (compare(top, list[i]) < 0) {
                top = list[i];
            }
        }
        return top;
    }
}

/**
 * 获取数组中的最顶级元素
 *
 * 顶级指的是最大值，还是最小值，取决于比较函数
 *
 * @example 取最大值写法
 *  ```typescript
 *  sortArray([3, 1, 2], (a, b) => a - b);
 *  ```
 *
 * @param list - 要排序的数组（不会被修改，会返回新数组）
 * @param compare - 比较函数，接受两个参数并返回一个数字：
 *   - 返回负数：使用 b 值
 *   - 返回正数：使用 a 值
 *   - 返回 0：使用 a 值
 * @returns 最顶级的元素，如果存在相同的，则取第一个
 */
const findTopElements = (list, compare) => {
    if (list == null) {
        return [];
    } else if (list.length === 1) {
        // copy element
        return [...list];
    } else {
        const top = findTopElement(list, compare);
        return list.filter(item => compare(top, item) === 0);
    }
}

/**
 * 冒泡排序
 *
 * @example 升序排序
 *  ```typescript
 *  sortArray([3, 1, 2], (a, b) => a - b);
 *  ```
 *
 *  @example 降序排序
 *  ```typescript
 *  sortArray([3, 1, 2], (a, b) => b - a);
 *  ```
 *
 * @param list - 要排序的数组（不会被修改，会返回新数组）
 * @param compare - 比较函数，接受两个参数并返回一个数字：
 *   - 返回负数：a 排在 b 前面
 *   - 返回正数：b 排在 a 前面
 *   - 返回 0：保持原顺序
 * @returns 排序后的新数组
 */
const sortArray = (list, compare) => {
    // 创建数组副本以避免修改原数组
    const arr = [...list];
    const len = arr.length;

    // 空数组或单元素数组直接返回
    if (len <= 1) {
        return arr;
    }

    // 升序
    for (let i = 0; i < len - 1; i++) {
        for (let j = 0; j < len - 1 - i; j++) {
            if (compare(arr[j], arr[j + 1]) > 0) {
                let temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}

/**
 * 获取数组中元素的字段值列表
 *
 * @param list      集合
 * @param field     字段名
 * @returns 至少是一个空的数组
 */
const getFieldValue = (list, field) => {
    let res = [];
    if (list != null && list.length > 0) {
        list.forEach(ele => res.push(ele[field]));
    }
    return res;
}

/**
 * 获取数组中元素的字段值列表
 *
 * 获取复数的字段值
 *
 * @param list      集合
 * @param fields    字段名
 * @returns 返回一个 map， value 值为对象属性值构成的数组
 */
const getFieldValues = (list, ...fields) => {
    const len = fields.length;
    if (len === 0) {
        return {};
    } else {
        const args = [];
        for (let i = 0; i < len; i++) {
            args.push([]);
        }
        list.forEach(item => {
            for (let i = 0; i < len; i++) {
                args[i].push(item[fields[i]]);
            }
        })
        const ret = {};
        for (let i = 0; i < len; i++) {
            ret[fields[i]] = args[i];
        }
        return ret;
    }
}

/**
 * 获取 id 串
 *
 * @param list 集合
 * @returns 由 id 构成的数组
 */
const getIds = (list) => {
    return getFieldValue(list, 'id');
}

/**
 * 获取 name 串
 *
 * @param list 集合
 * @returns 由 name 构成的数组
 */
const getNames = (list) => {
    return getFieldValue(list, 'name');
}

/**
 * 获取 label 串
 *
 * @param list 集合
 * @returns 由 label 构成的数组
 */
const getLabels = (list) => {
    return getFieldValue(list, 'label');
}

/**
 * 通用工具
 *
 * @constructor
 */
const ArrayUtils = {
    indexOf, contain, notContain, removeElement,
    findElement, findElements, findElementField,
    findTopElement, findTopElements,
    findByKeyword, findElementsByRange,
    wrapArray, groupByField, sortArray,
    getFieldValue, getFieldValues, getIds, getNames, getLabels
}

// 只是为了初始化
export default ArrayUtils;
